package main

import "fmt"

func array() {
	/*
		数据类型：
			基本类型：整数、浮点、布尔、字符串
			复合类型：array、slice、map、strut、ponter、function、channel...
		数组：
			1.概念：存储一组相同数据类型的数据结构，理解为容器，存储一组数据
			2.语法：
				var 数组名 [长度] 数据类型
				var 数组名 = [长度] 数据类型{元素1，元素2...}
				数组名 := [...]数据类型{元素1，元素2}
			3.访问：
				下标、也叫索引:index
				默认从0开始，直到长度减1
				数组名[index]
			4.长度和容量：go语言的内置函数
				len(array|map|)
				cap(array|map)
	*/
	//step1:创建数组
	var arr1 [4]int
	//step2:数组的访问
	arr1[0] = 1
	arr1[1] = 2
	arr1[2] = 3
	arr1[3] = 4
	fmt.Println("数组arr1", arr1)
	fmt.Println("数组arr1的第一个元素", arr1[0]) //打印第一个元素，访问数组时不能超过下标
	fmt.Println("数组arr1的第二个元素", arr1[1]) //打印第二个元素
	fmt.Println("数组arr1的长度", len(arr1))  //容器中实际存储的数据量
	fmt.Println("数组arr1的容量", len(arr1))  //容器中能够存储的最大的数量
	//因为数组是定长的，所以它的长度和容量相同
	//数组的其他创建方式
	var arr2 [4]int             //同 var arr2 = [4] int
	fmt.Println("数组arr2", arr2) //没有赋值时为0
	var arr3 = [4]int{1, 2, 3, 4}
	fmt.Println("数组arr3", arr3)
	arr4 := [...]int{1: 2, 3: 4} //[0 2 0 4]
	fmt.Println("数组arr4", arr4)

	/*
		依次访问数组中的元素
		方法一：arr[0]、arr[1]、arr[2]。。。
		方法二：通过循环，配合下标
			for i:=0;i<len(arr);i++ {
				arr[i]
			}
		方法三：使用range
			不需要操作数组的下标，到达数组的末尾，自动结束for range循环，每次都数组中获取下标和对应的数值。
	*/
	//方法一
	// arr1 := [5]int{1, 2, 3, 4, 5}
	fmt.Println("数组的访问方式1(下标):", arr1[0], arr1[1], arr1[2], arr1[3])
	fmt.Println("-------------")
	//方法二
	for i := 0; i < len(arr1); i++ {
		fmt.Println("数组的访问方式2(for循环):", arr1[i])
	}
	fmt.Println("-------------")
	//方法三:
	for index, value := range arr1 {
		fmt.Printf("下标是:%d,数值是:%d\n", index, value)
	}
	//计算数组元素的和
	sum := 0
	for _, value := range arr1 { //通过_舍弃这个下标
		sum += value
	}
	fmt.Printf("数组的和是：%d\n", sum)
}

func slice() {
	/*
		数组array:
			存储一组相同数据类型的数据结构
			特点：定长
		切片slice：
			同数组类似，也叫做变长数组或者动态数组
			是一个引用类型的容量，指向了一个底层数组
			1、每一个切片都引用一个底层数组
			2、切片本身不存储任何数据，都是底层数组存储，所以修改切片也就是修改数组中的数据
			3、当向切片中添加数据时，如果没有超过容量，直接添加，如果超过容量，自动扩容(成倍增长)
			4、切片一旦扩容，就是重新指向一个新的底层数组

		创建切片的方式一：
			var s1 []int
		创建切片的方式二:
			make([]int, 3, 10)
			第一个参数：类型
				slice、map、chan
			第二个参数：长度len
				实际存储元素的数量
			第三个参数：容量cap
				最多能够存储的元素数量
	*/
	//数组
	arr := [4]int{1, 2, 3, 4}
	fmt.Println(arr)
	//切片
	// s1 := []int{}
	var s1 []int // 等价于 s1 := []int{}
	fmt.Println(s1)
	s2 := []int{1, 2, 3, 4, 5} //变长
	fmt.Println(s2)
	fmt.Printf("%T,%T\n", arr, s2)
	s3 := make([]int, 3, 8)
	fmt.Println(s3)
	fmt.Printf("容量：%d，长度：%d\n", cap(s3), len(s3))
	s3[0] = 1
	s3[1] = 2
	s3[2] = 3
	fmt.Println(s3)
	//append() 给切片增加元素
	s4 := make([]int, 0, 10)
	fmt.Println(s4)
	s4 = append(s4, s3...) // ... 表示解析s3切片
	s4 = append(s4, 5, 6, 7, 8)
	fmt.Println(s4)
	fmt.Printf("%p\n", s4) // 因为s4是引用类型存储的是底层数组的地址
	a := 1
	fmt.Printf("%p\n", &a)
	fmt.Println(&a)
}

func mapmap() {
	/*
		map：映射,是一种专门用于存储键值对的集合、属于引用类型，
		存储特点：
			A：存储的是无序的键值对
			B：键不能重复，并且和value值一一对应的，map中的key不能重复，如果重复，那么新的value会覆盖原来的，程序不会报错
		语法结构：
			1、创建map
				var map1 map[key类型]value类型
					nil map,无法直接使用
				var map2 = make(map[key类型]value类型)
				var map3 = map[key类型]value类型{key:value,key:value......}
			2、添加/修改
				map[key] = value
					key存在就是修改数据、不存在就是添加数据
			3、获取
				map[key]
				最好是 v, ok = map[key]
			4、删除数据
				delete(map, key)
				若key存在就删除、不存在就不操作
		每种数据类型初始值：
			int：0
			float：0.0
			string：""
			array:[0,0,0]

			slice:nil
			map:nil
	*/
	//创建map

	var map1 map[int]string
	var map2 = make(map[int]string)
	var map3 = map[string]int{"Go": 100, "Python": 90, "Java": 80}
	fmt.Println(map1) //map[]
	fmt.Println(map2) //map[]
	fmt.Println(map3) //map[Go:100 Java:80 Python:90]
	// 存储键值对到map中
	//由于map1还没有初始化、直接储存键值对会报错、应该先初始化下
	if map1 == nil {
		map1 = make(map[int]string)
	}
	//添加键值对到map中
	map1[1] = "A"
	map1[2] = "B"
	fmt.Println(map1) // map[1:A 2:B]
	//获取数据，如果key存在，获取数值，不存在获取的value的零值
	fmt.Println(map1)    // map[1:A 2:B]
	fmt.Println(map1[1]) // "A"
	fmt.Println(map1[3]) //value是string它的零值是空
	v1, ok := map1[4]    // 1.16版本可以直接不用ok
	if ok {
		fmt.Printf("存在key的值%s\n", v1)
	} else {
		fmt.Printf("不存在key的值%s\n", v1)
	}
	//修改数据
	map1[1] = "C"
	fmt.Println(map1) //map[1:C 2:B]
	//删除数据
	delete(map1, 2)
	fmt.Println(map1) //map[1:C]
	/*
		map的遍历
			使用:for range
			数组、切片、index，value
			map：key，value
	*/
	map4 := make(map[int]string)
	map4[1] = "A"
	map4[2] = "B"
	map4[3] = "C"

	//1.遍历
	for k, v := range map4 {
		fmt.Println(k, v)
	}
}

func main() {
	fmt.Println("练习1-----hello world------")
	fmt.Println("Hello,World!!")
	fmt.Println("-------end------")
	fmt.Println("练习2------数组-------")
	array()
	fmt.Println("------end------")
	fmt.Println("练习3------切片-------")
	slice()
	fmt.Println("------end------")
	fmt.Println("练习4------map-------")
	mapmap()
	fmt.Println("------end------")

}
// 可以尝试下不同的打印方法，只提交作业代码即可